#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
解析管理器(ParserManager)：用于管理整个代码解析和知识图谱构建流程
负责人：邓文千可

主要功能：
1. 管理两阶段解析流程:
   - 第一阶段: 扫描目录建立class_list
   - 第二阶段: 详细解析每个类并构建知识图谱
2. 维护全局数据:
   - class_list: 所有类的信息列表
   - class_name_map: 类名到类信息的映射
3. 处理类依赖关系:
   - 解析器栈管理
   - 依赖类优先解析
   - 外部依赖处理
4. 资源管理:
   - 解析器生命周期管理
   - 内存资源释放
   - 线程安全保证

优化调整：
1. 架构优化
    -采用两阶段解析策略，将扫描和解析分离
    -引入解析器栈管理机制，支持依赖类优先解析
    -使用弱引用管理类信息，避免内存泄漏
    -增加线程安全保护，使用 RLock 实现
2. 数据结构优化
    -引入 ParseResult 数据类，统一管理解析结果
    -使用 defaultdict 优化错误消息管理
3. 类名解析优化
    -使用 @lru_cache 缓存类名查找结果
4. 资源管理优化
    -实现上下文管理器接口（__enter__/__exit__）
    -完善资源清理机制
    -支持自动垃圾回收
    -优化组件生命周期管理
5. 错误处理优化
    -引入自定义异常类层次结构
    -完善错误信息记录机制
    -增加详细的日志记录
"""
import copy
import os
import threading
import logging
import parser.refactor.name_resolver as name_resolver
from typing import Dict, List, Set, Any, Optional, Tuple, Type, cast
from dataclasses import dataclass, field, fields
from enum import Enum, auto
from functools import lru_cache
import weakref
from collections import defaultdict

# 导入数据结构
from parser.refactor.data_structures import ClassInfo, ParserContext, DependencyContext, MetadataInfo, \
    GraphNodeInfo, DependencyType, RelationshipInfo, GraphEdgeInfo, PackageInfo
# 其他导入
from parser.refactor.java_scanner import JavaScanner
from parser.refactor.java_parser import JavaParser
from parser.refactor.graph import GraphConverter

logger = logging.getLogger(__name__)

class ParserPhase(Enum):
    """
    解析阶段枚举

    SCANNING: 第一阶段,扫描目录
    PARSING: 第二阶段,详细解析
    COMPLETED: 解析完成
    FAILED: 解析失败
    """
    SCANNING = auto()
    PARSING = auto()
    COMPLETED = auto()
    FAILED = auto()

class ParserError(Exception):
    """解析器错误基类"""
    pass

class InitializationError(ParserError):
    """初始化错误"""
    pass

class ParseError(ParserError):
    """解析错误"""
    pass

@dataclass
class ParseResult:
    """
    解析结果数据类，用于统一管理解析结果
    
    属性:
        success_count: 成功解析的类数量
        fail_count: 解析失败的类数量
        error_messages: 错误信息列表
        phase: 当前解析阶段
    """
    success_count: int = 0
    fail_count: int = 0
    error_messages: List[str] = field(default_factory=list)
    phase: ParserPhase = ParserPhase.SCANNING

    def add_error(self, message: str) -> None:
        """添加错误信息"""
        self.error_messages.append(message)
        self.fail_count += 1

    def add_success(self) -> None:
        """添加成功计数"""
        self.success_count += 1

class ParserManager:
    """
    代码解析管理器，负责协调整个解析过程
    
    主要职责：
    1. 管理解析器的生命周期
    2. 协调目录扫描和详细解析两个阶段
    3. 处理类之间的依赖关系
    4. 维护解析上下文栈
    5. 错误处理和日志记录

    线程安全说明:
    - 使用RLock保护共享数据访问
    - 批量操作保证原子性
    - 使用弱引用避免内存泄漏
    """
    
    def __init__(self, repo_path: str, graph_connection, language: str = 'java'):
        """
        初始化解析管理器
        
        Args:
            repo_path: 代码仓库路径
            graph_connection: 图数据库连接，用于存储解析结果
            language: 编程语言，默认为Java，会自动转换为小写
            
        初始化数据结构：
        1. _class_list: 存储所有类信息
        2. parser_stack: 解析器上下文栈
        3. _class_name_map: 类名到类信息的映射，用于优化查找
        4. _processed_classes: 已处理的类集合
        5. _processed_files: 已处理的文件集合
        6. _error_messages: 错误信息列表
        """
        # 邓文千可：初始化必要的组件和数据结构
        self.repo_path = repo_path
        self.graph_connection = graph_connection
        self.language = language.lower()

        # 使用强引用存储类信息，避免弱引用导致对象被回收
        self._lock = threading.RLock()  # 使用可重入锁
        self._class_list: List[ClassInfo] = []
        self._package_list: List[PackageInfo] = []
        self._class_name_map: Dict[str, ClassInfo] = {} # 类名到类信息的映射
        self._metadata_map: Dict[str, List[MetadataInfo]] = {} # 类名到元数据信息的映射

        # 简化错误消息处理
        self._error_messages: defaultdict[str, List[str]] = defaultdict(list)

        # 解析器上下文栈
        self.parser_stack: List[ParserContext] = []
        self.max_stack_depth = 1  # 限制栈深度为1
        
        # 解析器实例
        self.parser: Optional[Any] = None  # 具体语言的解析器
        self.scanner: Optional[Any] = None  # 目录扫描器
        self.graph: Optional[Any] = None  # 图转换器
        
        # 使用集合存储已处理的项，提高查找效率
        self._metadata_extracted_classes: Set[str] = set()  # 元数据提取完成的类集合
        self._relationships_extracted_classes: Set[str] = set()  # 关系提取完成的类集合
        self._processed_classes: Set[str] = set()  # 已完全处理的类集合
        self._processed_files: Set[str] = set()  # 已处理的文件集合
        
        # 初始化组件
        self._initialize_components()

        # 当前解析阶段
        self.current_phase = ParserPhase.SCANNING

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        上下文管理器退出时清理资源

        Returns:
            False: 让异常继续传播
        """
        self._cleanup_resources()
        return False
    
    def _initialize_components(self) -> None:
        """
        初始化各组件
        
        根据语言类型初始化相应的解析器、扫描器和图转换器
        目前仅支持Java语言
        - 解析器(parser)
        - 扫描器(scanner)
        - 图转换器(graph)
        
        Raises:
            InitializationError: 初始化失败时
        """
        # 邓文千可：根据language初始化相应的解析器和扫描器
        try:
            if self.language == 'java':
                # 初始化Java解析器
                self.parser = JavaParser(self.graph_connection, self.repo_path)
                # 初始化目录扫描器
                self.scanner = JavaScanner(self.repo_path)
                # 初始化图转换器
                self.graph = GraphConverter()
            else:
                raise ValueError(f"不支持的语言: {self.language}")
        except Exception as e:
            logger.error(f"初始化组件失败: {str(e)}")
            raise InitializationError(f"初始化组件失败: {str(e)}") from e

    @property
    def class_list(self) -> List[ClassInfo]:
        """
        线程安全地获取类列表

        Returns:
            List[ClassInfo]: 活跃的类信息列表
        """
        with self._lock:
            return list(self._class_list)

    def add_class(self, class_info: ClassInfo) -> None:
        with self._lock:
            self._class_list.append(class_info)
            self._class_name_map[class_info.full_name] = class_info
            logger.debug(f"添加类到管理器: {class_info.full_name}")

    def update_class(self, src_class_info: ClassInfo, target_class_info: ClassInfo) -> None:
        """
        Args:
            src_class_info: 需要更新的class_info
            target_class_info: 刚抽取的class_info
        """

        # 定义需要深拷贝的属性
        fields_to_deepcopy = {'imports', 'methods', 'super_classes', 'implemented_interfaces', 'inner_classes',
                              'annotations'}

        for field in fields(target_class_info):
            field_name = field.name
            if hasattr(src_class_info, field_name):
                if field_name in fields_to_deepcopy:
                    # 对于需要深拷贝的属性，调用相应的copy方法
                    setattr(src_class_info, field_name, getattr(target_class_info, field_name).copy())
                else:
                    # 其他属性直接赋值
                    setattr(src_class_info, field_name, getattr(target_class_info, field_name))

    
    def run(self) -> ParseResult:
        """
        运行完整的解析流程
        
        执行两阶段解析：
        1. 目录扫描阶段：建立class_list
        2. 详细解析阶段：构建知识图谱
        
        Returns:
            ParseResult: 包含成功数、失败数和错误信息的解析结果
            
        Raises:
            Exception: 当解析过程发生错误时
        """
        result = ParseResult()
        # 邓文千可：实现两阶段解析流程
        try:
            # 第一阶段：目录扫描，建立class_list
            self._phase1_directory_scan(result)
            if result.phase == ParserPhase.FAILED:
                return result
            
            # 第二阶段：详细解析，构建知识图谱
            self._phase2_detailed_parse(result)
        except Exception as e:
            result.add_error(f"解析过程发生错误: {str(e)}")
            result.phase = ParserPhase.FAILED
            logger.error(f"解析失败: {str(e)}", exc_info=True)

        return result
    
    def _phase1_directory_scan(self, result: ParseResult) -> None:
        """
        第一阶段：扫描目录建立class_list

        使用scanner扫描项目目录:
        1. 获取所有类的基本信息
        2. 构建类名映射
        3. 快速提取包名信息

        Args:
            result: 用于记录解析结果

        Raises:
            ParseError: 扫描失败时
        """
        # 邓文千可：使用scanner扫描项目中所有相关文件，获取基础类信息
        logger.info("开始第一阶段：目录扫描...")
        
        try:
            # 使用目录扫描器获取类列表
            scanned_classes, scanned_packages = self.scanner.scan_directory()
            for class_info in scanned_classes:
                self.add_class(class_info)
            self._package_list = scanned_packages
            result.phase = ParserPhase.PARSING
            logger.info(f"第一阶段完成，发现 {len(scanned_classes)} 个类")

        except Exception as e:
            result.add_error(f"目录扫描失败: {str(e)}")
            result.phase = ParserPhase.FAILED
            raise ParseError("目录扫描失败") from e
    
    def _phase2_detailed_parse(self, result: ParseResult) -> None:
        """
        第二阶段：详细解析每个类，构建知识图谱
        
        遍历class_list中的每个类，进行详细解析：
        1. 跳过已解析的类
        2. 解析文件生成语法树
        3. 提取元数据和关系
        4. 转换为图数据库
        5. 完成后清理资源
        
        Args:
            result: 用于记录解析结果
        """
        # 邓文千可：实现对每个类的详细解析流程
        try:
            logger.info("开始第二阶段：详细解析...")

            # 遍历class_list中的每个类，进行详细解析
            for class_info in self.class_list:
                if self._should_skip_class(class_info):
                    continue

                try:
                    if self.process_class(class_info):
                        result.add_success()
                    else:
                        result.add_error(f"解析类 {class_info.full_name} 失败")
                except Exception as e:
                    result.add_error(f"处理类 {class_info.full_name} 时出错: {str(e)}")

            result.phase = ParserPhase.COMPLETED
            logger.info(f"第二阶段完成，成功: {result.success_count}，失败: {result.fail_count}")

        finally:
            # 完成后清理资源
            self._cleanup_resources()
    
    def _should_skip_class(self, class_info: ClassInfo) -> bool:
        """
        判断是否应该跳过类的解析
        
        Args:
            class_info: 要检查的类信息
            
        Returns:
            bool: 如果类已解析或已处理，返回True
        """
        return class_info.is_parsed or class_info.full_name in self._processed_classes
    
    def process_class(self, class_info: ClassInfo, extract_relationships: bool = True, convert_to_graph: bool = True,
                      current_metadata_list=None) -> bool:
        """
        处理单个类
        
        处理流程：
        1. 解析文件生成语法树
        2. 提取元数据
        3. 提取关系（可选）
        4. 转换为图数据库
        5. 标记类为已处理
        
        Args:
            class_info: 类信息对象
            extract_relationships: 是否需要提取关系，在处理依赖类时可以设为False
            current_metadata_list: 元数据列表

        Returns:
            bool: 处理是否成功
        """
        # 邓文千可：实现单个类的处理逻辑

        # 解析类依赖的元数据信息会使用到
        if current_metadata_list is None:
            current_metadata_list = []

        try:
            # 获取类信息
            file_path = class_info.file_path
            full_name = class_info.full_name
            
            logger.info(f"开始解析类: {full_name}")
            
            # 解析文件生成语法树
            tree, code_bytes = self.parser.parse_file(file_path)
            if not tree:
                self._add_error("parse_error", f"解析文件失败: {file_path}")
                return False
            # 保存源码字节到class_info（如有source_code字段）
        
            class_info.source_code = code_bytes.decode('utf-8') if code_bytes else None

            # TODO dwqk metadata 缓存优化?
            # 提取元数据（如果未提取过）
            metadataList = None
            if not class_info.metadata_extracted and full_name not in self._metadata_extracted_classes:
                logger.info(f"提取类元数据: {full_name}")
                metadata_list = self.parser.extract_metadata(tree, file_path, self._class_name_map, code_bytes, parser_manager=self, current_metadata_list = current_metadata_list)
                # 更新元数据提取状态
                print(f": 当前类:{class_info.name}, 当前解析返回元数据:{current_metadata_list}")
                self.after_extract_metadata(metadata_list, class_info)

            # 提取关系（如果需要且未提取过）
            relationships = None
            result = True # 避免未分配值
            if extract_relationships and not class_info.relationships_extracted and full_name not in self._relationships_extracted_classes:
                if not metadataList:
                    # 如果之前已提取过元数据，需要重新获取
                    metadataList = self._metadata_map[full_name]
                
                logger.info(f"提取类关系: {full_name}")
                # 注意：在extract_relationships过程中可能发现未解析的类引用
                # 此时JavaParser会调用下面的handle_dependency_class方法
                relationships = self.parser.extract_relationships(tree, file_path, metadataList, code_bytes, parser_manager=self)
                
                # 更新关系提取状态
                class_info.relationships_extracted = True
                self._relationships_extracted_classes.add(full_name)
            
            if convert_to_graph:
            # 转换为图数据库
                result = self.graph.convert_to_graph(metadataList, relationships)
                if not result:
                    self._add_error("graph_error", f"转换图失败: {class_info.full_name}")
                    return False

            # 更新状态
            self._mark_class_as_processed(class_info)
            
            logger.info(f"类 {full_name} 解析完成")
            return result
        except Exception as e:
            self._add_error("process_error", f"处理类失败: {str(e)}")
            logger.error(f"处理类 {class_info.full_name} 时出错", exc_info=True)
            return False

    def _mark_class_as_processed(self, class_info: ClassInfo) -> None:
        """
        标记类为已处理状态

        只有当类的元数据和关系都提取完成时，才将类标记为已处理。
        这样可以确保类的完整信息都被正确提取。

        Args:
            class_info: 要标记的类信息
        """
        # 检查元数据和关系是否都已提取
        if class_info.metadata_extracted and class_info.relationships_extracted:
            with self._lock:
                # 添加到已处理集合
                self._processed_classes.add(class_info.full_name)
                self._processed_files.add(class_info.file_path)
                class_info.is_parsed = True
                logger.debug(f"类 {class_info.full_name} 已完全处理")
        else:
            logger.debug(
                f"类 {class_info.full_name} 未完全处理: "
                f"metadata_extracted={class_info.metadata_extracted}, "
                f"relationships_extracted={class_info.relationships_extracted}"
            )

    def _add_error(self, error_type: str, message: str) -> None:
        """添加错误信息"""
        self._error_messages[error_type].append(message)
        logger.error(message)
    
    def handle_dependency_class(self, dependent_class_name: str, current_context: ParserContext) -> bool:
        """
        处理解析过程中发现的类依赖关系。
        
        处理流程:
        1. 检查依赖类是否在当前解析的类列表中
        2. 如果不在类列表中:
           - 创建外部依赖类的节点信息(GraphNodeInfo)
           - 构建依赖上下文(DependencyContext)
           - 记录外部依赖关系
        3. 如果在类列表中且未解析:
           - 保存当前解析上下文
           - 优先解析依赖类
           - 恢复原解析上下文
        4. 如果在类列表中且已解析，直接返回成功

        Args:
            dependent_class_name: 依赖类的完整类名(包含包路径)
            current_context: 当前的解析上下文，包含当前正在解析的类、方法等信息

        Returns:
            bool: 处理是否成功
                - True: 成功处理依赖(创建外部依赖/已处理/解析成功)
                - False: 处理过程中发生错误

        Raises:
            可能抛出的异常会被捕获并记录日志，返回False
        """
        try:
            # 检查栈深度
            if len(self.parser_stack) >= self.max_stack_depth:
                logger.info(f"已达到最大栈深度({self.max_stack_depth})，跳过解析依赖类: {dependent_class_name}")
                return True

            dependent_class = name_resolver.resolve_full_qualified_name(dependent_class_name, current_context.current_class, self._package_list, self._class_list)
            
            if not dependent_class:
                # 处理外部依赖类...
                logger.info(f"发现外部依赖类: {dependent_class_name}")
                
                # 创建外部依赖类的节点信息
                node_info = GraphNodeInfo(
                    node_id=dependent_class_name,
                    node_type="ExternalClass",
                    properties={
                        "name": dependent_class_name.split(".")[-1],  # 类名
                        "full_name": dependent_class_name,            # 完整类名
                        "package": ".".join(dependent_class_name.split(".")[:-1]),  # 包路径
                        "is_external": True                           # 标记为外部依赖
                    }
                )
                
                # 创建依赖上下文，记录依赖发现时的详细信息
                dependency_context = DependencyContext(
                    source_class=current_context.current_class.full_name,  # 依赖源类
                    dependent_class=dependent_class_name,                  # 被依赖类
                    file_path=current_context.current_class.file_path,    # 源文件路径
                    dependency_type=DependencyType.IMPORT,                # 依赖类型(导入)
                    package=current_context.current_class.package
                )
                
                # 记录外部依赖关系
                self._record_external_dependency(node_info, dependency_context)
                return True
                
            # 如果依赖类已经提取过元数据，则直接返回成功
            if dependent_class.metadata_extracted or dependent_class.full_name in self._metadata_extracted_classes:
                logger.info(f"依赖类 {dependent_class_name} 已提取元数据，跳过处理")
                return True
            
            # 处理内部依赖类...
            if self._should_skip_class(dependent_class):
                return True
            
            # 保存当前解析上下文（压栈）
            self.push_context(current_context)
            
            # 优先解析依赖类（只提取元数据，不提取关系）
            logger.info(f"发现未解析的依赖类 {dependent_class_name}，优先提取元数据")
            success = self.process_class(dependent_class, extract_relationships=False, convert_to_graph=True)
            
            # 恢复之前的解析上下文（出栈）
            restored_context = self.pop_context()
            
            # 检查是否成功恢复上下文
            if not restored_context:
                self._add_error("context_error", "恢复解析上下文失败")
                return False
            
            logger.info(f"恢复解析类: {restored_context.current_class.full_name}")
            return success
        except Exception as e:
            self._add_error("dependency_error", f"处理依赖类失败: {str(e)}")
            return False
    
    def _record_external_dependency(self, node_info: GraphNodeInfo, dependency_context: DependencyContext) -> None:
        """
        记录外部依赖关系到知识图谱。
        
        工作流程:
        1. 根据依赖上下文创建关系信息(RelationshipInfo)
        2. 将关系添加到关系集合(RelationshipCollection)
        3. 创建图数据库边信息(GraphEdgeInfo)
        4. 使用图转换器在数据库中创建节点和边
        
        实现细节:
        - 使用完整类名作为节点ID
        - 记录依赖关系的详细属性(文件路径、行号、包信息等)
        - 通过图转换器统一处理图数据库操作
        
        Args:
            node_info: 外部依赖类的节点信息，包含节点ID、类型和属性
            dependency_context: 依赖上下文，包含源类、目标类、依赖类型等信息

        注意事项:
        - 确保在创建边之前节点已存在
        - 所有异常都会被捕获并记录日志
        - 边的属性来自dependency_context，便于后续分析和查询
        """
        try:
            # 创建关系信息，记录依赖的详细属性
            relationship = RelationshipInfo(
                source_id=dependency_context.source_class,
                target_id=dependency_context.dependent_class,
                relationship_type=dependency_context.dependency_type.value,
                properties={
                    "file_path": dependency_context.file_path,      # 源文件路径
                    "package": dependency_context.package           # 包信息
                }
            )
            
            # 创建图数据库边信息
            edge_info = GraphEdgeInfo(
                src_id=dependency_context.source_class,
                dst_id=dependency_context.dependent_class,
                edge_type=dependency_context.dependency_type.value,
                properties=relationship.properties
            )
            
            # 使用图转换器创建节点和边
            self.graph.create_node(node_info)
            self.graph.create_edge(edge_info)
            
            logger.debug(f"成功记录外部依赖: {dependency_context.source_class} -> {dependency_context.dependent_class}")
            
        except Exception as e:
            self._add_error("dependency_error", f"记录外部依赖关系失败: {str(e)}")

    def after_extract_metadata(self, metadata_list: List[MetadataInfo], main_class_info: ClassInfo) -> bool:
        """
        抽取元数据后的操作
        1. 更新_metadata_extracted_classes
        2. 更新_class_list
        3. 更新_class_name_map

        Args:
            metadata_list: 提取的元数据列表
            main_class_info: 当前提取文件的主类classInfo

        Returns:
            处理是否成功
        """

        for metadata in metadata_list:
            if metadata:
                class_info = metadata.class_info
                if class_info:
                    logger.info(f"完成抽取, 进行更新: {class_info.full_name}")
                    self._metadata_map[class_info.full_name] = metadata_list 
                    class_info.metadata_extracted = True
                    self._metadata_extracted_classes.add(class_info.full_name)
                    if class_info.full_name == main_class_info.full_name:
                        # 更新原来的class_info
                        self.update_class(main_class_info, class_info)
                    else:
                        # 内部类, 新增class_info
                        self.add_class(class_info)

        return True
    
    def push_context(self, context: ParserContext) -> None:
        """
        将当前解析上下文压入栈
        
        Args:
            context: 解析上下文
        """
        # 邓文千可：实现上下文压栈
        self.parser_stack.append(context)
        logger.debug(f"上下文压栈，当前栈深度: {len(self.parser_stack)}")
    
    def pop_context(self) -> Optional[ParserContext]:
        """
        从栈中弹出解析上下文
        
        Returns:
            Optional[ParserContext]: 解析上下文或None
        """
        # 邓文千可：实现上下文出栈
        if self.parser_stack:
            context = self.parser_stack.pop()
            logger.debug(f"上下文出栈，当前栈深度: {len(self.parser_stack)}")
            return context
        return None

    def _cleanup_resources(self) -> None:
        """
        清理解析完成后的资源

        清理内容:
        1. 类信息相关数据结构
        2. 解析器组件
        3. 解析上下文栈
        4. 错误消息
        5. 触发垃圾回收
        """
        with self._lock:
            try:
                # 1. 清理类信息
                self._class_list.clear()
                self._class_name_map.clear()
                self._metadata_extracted_classes.clear()
                self._relationships_extracted_classes.clear()
                self._processed_classes.clear()
                self._processed_files.clear()

                # 2. 清理解析器相关资源
                if self.parser:
                    self.parser = None
                if self.scanner:
                    self.scanner = None
                if self.graph:
                    self.graph = None

                # 3. 清理解析上下文栈
                self.parser_stack.clear()

                # 4. 清理错误消息
                self._error_messages.clear()

                # 5. 强制进行垃圾回收
                import gc
                gc.collect()

                logger.info("资源清理完成")

            except Exception as e:
                logger.error(f"清理资源时出错: {str(e)}")